import time
import sched
import socket
import struct
import threading
import numpy as np


class DataStream(object):
    def __init__(self, processor):
        self.ip = 'localhost'
        self.port = 9889
        self.processor = processor
        self.NSocket = socket.socket()
        self.NSocket.settimeout(15)
        self.sche = sched.scheduler()
        self.sche.enter(0, 1, self.on_sched)  # delay, priority, action
        self.thread = threading.Thread(target=self.sche.run)
        self.connected = False
        self.signal = []
        self.count = 0
        self.endTime = 0
        self.timestamp = []

    def start(self):
        self.onConnect()
        if self.connected:
            print('Data Server Ready')
        self.startTime = time.clock()
        self.thread.start()

    def on_sched(self):
        now = time.monotonic()
        self.sche.enterabs(now + self.T, 1, self.on_sched)
        try:
            self.on_data_read()
        except Exception as e:
            print(str(e))
        self.endTime = time.clock()
        # self.timestamp = np.concatenate((self.timestamp, np.arange(self.endTime)))

    def onConnect(self):
        try:
            self.NSocket.connect((self.ip, self.port))
        except Exception as e:
            print(e.strerror)
            print("Data Server Connection Failed")
            self.connected = False
            return
        else:
            print("Data Server Connection Completed")
            self.NSocket.settimeout(None)
            self.connected = True

        self.send_command_to_ns(3, 5)
        time.sleep(0.1)
        # get basic information
        self.BasicInfo = self.NSocket.recv(100)
        code = int.from_bytes(self.BasicInfo[4:6], byteorder='big')
        req = int.from_bytes(self.BasicInfo[6:8], byteorder='big')
        size = int.from_bytes(self.BasicInfo[8:12], byteorder='big')
        if code == 1 and req == 3 and size == 28 and len(self.BasicInfo) == 40:
            self.Bsize = int.from_bytes(self.BasicInfo[12:16], byteorder='little')
            self.BEegChannelNum = int.from_bytes(self.BasicInfo[16:20], byteorder='little')
            self.BEventChannelNum = int.from_bytes(self.BasicInfo[20:24], byteorder='little')
            self.BlockPnts = int.from_bytes(self.BasicInfo[24:28], byteorder='little')
            self.BSampleRate = int.from_bytes(self.BasicInfo[28:32], byteorder='little')
            self.BDataSize = int.from_bytes(self.BasicInfo[32:36], byteorder='little')
            self.BResolution = struct.unpack('<f', self.BasicInfo[36:40])[0]
            self.channelNum = [self.BEegChannelNum, self.BEventChannelNum]
            self.T = self.BlockPnts / self.BSampleRate / 2
            self.processor.read_head_settings(self.BSampleRate, self.channelNum)
        self.send_command_to_ns(2, 1)
        self.send_command_to_ns(3, 3)

    def on_data_read(self):
        while True:
            data_head = self.NSocket.recv(12)
            if len(data_head) != 0:
                break
        size = int.from_bytes(data_head[8:12], byteorder='big')
        total = 0
        Data = bytearray()

        while total < size:
            a = self.NSocket.recv(size - total)
            Data = Data + a
            total = total + len(a)
        if self.BDataSize == 4:
            data = [i[0] * self.BResolution for i in struct.iter_unpack('<i', Data)]
        elif self.BDataSize == 2:
            data = [i[0] * self.BResolution for i in struct.iter_unpack('<h', Data)]
        self.processor.read_signal(data)

    def configure(self, frez=16):
        self.T = 1.0/frez
        # print(self.T)  # 0.0625

    def stop(self):
        if self.connected:
            self.send_command_to_ns(3, 4)
            self.send_command_to_ns(2, 2)
            self.send_command_to_ns(1, 2)
        print('Data Server Disconnected')
        for i in self.sche.queue:
            self.sche.cancel(i)
        self.thread.join()

    # def onDisconnect(self):
    #     pass
        # if self.exoskeletonData['exoskeletonFeedback']:  # — 连机械手Step 3 --
        #     if self.ES:
        #         if self.ES.Connected:
        #             self.ES.Disconnected()

    def send_command_to_ns(self, ctrcode, reqnum):
        a = 'CTRL'
        Cmd = a.encode(encoding="utf-8")
        Cmd += (ctrcode).to_bytes(2, 'big')
        Cmd += (reqnum).to_bytes(2, 'big')
        Cmd += (0).to_bytes(4, 'big')
        self.NSocket.sendall(Cmd)

    def get_signal(self):
        return np.array(self.signal)
